﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SlimDX;
using System.Threading;

using DX10 = SlimDX.Direct3D10;
using DXGI = SlimDX.DXGI;
using System.Diagnostics;

namespace TriDVisualizer
{
    public partial class TriVisUserControl : UserControl
    {
        private DX10.Device m_device;
        private DXGI.SwapChainDescription m_swapChainDesc;
        private DXGI.SwapChain m_swapChain;
        private DXGI.Factory m_factory;
        private DX10.RenderTargetView m_renderTarget;
        private bool m_initialized;

        private TriMesh m_triMesh;

        private Matrix m_viewMatrix;
        private Matrix m_projMatrix;
        private Matrix m_worldMatrix;
        private Matrix m_viewProjMatrix;

        private bool EnableMovie;
        private int MovieFrameCounter;
        private string MovieName;
 

        public TriVisUserControl()
        {
            InitializeComponent();

            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.Opaque, true);

            EnableMovie = false;
            MovieFrameCounter=0;
        }

        /// <summary>
        /// Initializes device and other resources needed for rendering. Returns true, if successful.
        /// </summary>
        private bool Initialize3D()
        {
            try
            {
                m_device = new DX10.Device(DX10.DriverType.Warp, DX10.DeviceCreationFlags.SingleThreaded);

                m_factory = new DXGI.Factory();

                m_swapChainDesc = new DXGI.SwapChainDescription();
                m_swapChainDesc.OutputHandle = this.Handle;
                m_swapChainDesc.IsWindowed = true;
                m_swapChainDesc.BufferCount = 1;
                m_swapChainDesc.Flags = DXGI.SwapChainFlags.AllowModeSwitch;
                m_swapChainDesc.ModeDescription = new DXGI.ModeDescription(
                    this.Width,
                    this.Height,
                    new Rational(60, 1),
                    DXGI.Format.R8G8B8A8_UNorm);
                m_swapChainDesc.SampleDescription = new DXGI.SampleDescription(1,0);
                m_swapChainDesc.SwapEffect = DXGI.SwapEffect.Discard;
                m_swapChainDesc.Usage = DXGI.Usage.RenderTargetOutput;

                m_swapChain = new DXGI.SwapChain(m_factory, m_device, m_swapChainDesc);

               

                DX10.Viewport viewPort = new DX10.Viewport();
                viewPort.X = 0;
                viewPort.Y = 0;
                viewPort.Width = this.Width;
                viewPort.Height = this.Height;
                viewPort.MinZ = 0f;
                viewPort.MaxZ = 1f;

                //DX10.Texture2D backBuffer = m_swapChain.GetBuffer<DX10.Texture2D>(0);
                DX10.Texture2D Texture = DX10.Texture2D.FromSwapChain<DX10.Texture2D>(m_swapChain,0);
                
                //m_renderTarget = new DX10.RenderTargetView(m_device, backBuffer);
                //DX10.RenderTargetViewDescription renderDesc = new DX10.RenderTargetViewDescription();
                //renderDesc.FirstArraySlice = 0;
                //renderDesc.MipSlice = 0;

                m_renderTarget = new DX10.RenderTargetView(m_device, Texture);

                Texture.Dispose();

                DX10.RasterizerStateDescription rsd = new DX10.RasterizerStateDescription();
                rsd.CullMode = DX10.CullMode.None;
                rsd.FillMode = DX10.FillMode.Solid;
                rsd.IsMultisampleEnabled = true;
                rsd.IsAntialiasedLineEnabled = false;
                rsd.IsDepthClipEnabled = false;
                rsd.IsScissorEnabled = false;

               DX10.RasterizerState RasterStateWireFrame = DX10.RasterizerState.FromDescription(m_device,rsd);

                DX10.BlendStateDescription blendDesc = new DX10.BlendStateDescription();
                blendDesc.BlendOperation = DX10.BlendOperation.Add;
                blendDesc.AlphaBlendOperation = DX10.BlendOperation.Add;
                blendDesc.SourceAlphaBlend = DX10.BlendOption.Zero;
                blendDesc.DestinationAlphaBlend = DX10.BlendOption.Zero;
                blendDesc.SourceBlend = DX10.BlendOption.SourceColor;
                blendDesc.DestinationBlend = DX10.BlendOption.Zero;
                blendDesc.IsAlphaToCoverageEnabled = false;
                blendDesc.SetWriteMask(0, DX10.ColorWriteMaskFlags.All);
                blendDesc.SetBlendEnable(0, true);
                DX10.BlendState m_blendState = DX10.BlendState.FromDescription(m_device, blendDesc);

                m_device.Rasterizer.State = RasterStateWireFrame;
                m_device.Rasterizer.SetViewports(viewPort);
                m_device.OutputMerger.BlendState = m_blendState;
                m_device.OutputMerger.SetTargets(m_renderTarget);

                m_viewMatrix = Matrix.LookAtLH(
                    new Vector3(0f, 0f, -1f),
                    new Vector3(0f, 0f, 1f),
                    new Vector3(0f, 1f, 0f));
                m_projMatrix = Matrix.PerspectiveFovLH(
                    (float)Math.PI * 0.5f,
                    this.Width / (float)this.Height,
                    0.1f, 100f);
                m_viewProjMatrix = m_viewMatrix * m_projMatrix;
                m_worldMatrix = Matrix.RotationYawPitchRoll(0f, 0f, 0f);

                //m_simpleBox = new SimpleBox();
                m_triMesh = new TriMesh();
                m_triMesh.LoadResources(m_device,"InverseLibraries\\Default");

                m_initialized = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error while initializing Direct3D10: \n" + ex.Message);
                m_initialized = false;
            }

            return m_initialized;
        }

        public void SetMesh(string path)
        {
            m_triMesh = new TriMesh();
            m_triMesh.LoadResources(m_device, path);
            m_triMesh.UpdateResources(m_device);
            this.Invalidate();
        }
        /// <summary>
        /// Rendering is done during the standard OnPaint event
        /// </summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (m_initialized)
            {
                m_device.ClearRenderTargetView(m_renderTarget, new Color4(Color.CornflowerBlue));

                m_triMesh.Render(m_device, m_worldMatrix, m_viewProjMatrix);

                m_swapChain.Present(0, DXGI.PresentFlags.None);
            }
        }



        /// <summary>
        /// Initialize 3D-Graphics within OnLoad event
        /// </summary>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            Initialize3D();
        }

        public void UpdateValue(float[] dataIn)
        {
            try
            {
                m_triMesh.UpdateValues(dataIn);

                if (InvokeRequired)
                {
                    this.Invoke(new MethodInvoker(delegate() { m_triMesh.UpdateResources(m_device); }));
                }
                else
                {
                    m_triMesh.UpdateResources(m_device);
                }
                if (EnableMovie == true)
                {
                    if (InvokeRequired)
                    {
                        this.Invoke(new MethodInvoker(delegate() { TakeSnapShot(MovieName + MovieFrameCounter.ToString()); }));
                    }
                    else
                    {
                        TakeSnapShot(MovieName + MovieFrameCounter.ToString());
                    }
                    
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e);
            }
        }
        public void StartMovieRecording(string name)
        {
            MovieFrameCounter = 0;
            EnableMovie = true;
            MovieName = name;
        }
        public void DisableAutoScale()
        {
            m_triMesh.DisableAutoScale();
        }
        public void ResetTheshold()
        {
            m_triMesh.ResetTheshold();
        }

        public void PlusTheshold()
        {
            m_triMesh.PlusTheshold();
        }
        public void MinusTheshold()
        {
            m_triMesh.MinusTheshold();
        }


        private void TakeSnapShot(string name)
        {
            MovieFrameCounter++;
            System.Drawing.Bitmap Bitmap;
            System.Drawing.Graphics Graps;
            Bitmap = new System.Drawing.Bitmap(this.Size.Width, this.Size.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            Graps = System.Drawing.Graphics.FromImage(Bitmap);
            System.Drawing.Point screenCoord = this.PointToScreen(this.Location);
            Graps.CopyFromScreen(screenCoord.X, screenCoord.Y, 0, 0, this.Size, System.Drawing.CopyPixelOperation.SourceCopy);
            //Bitmap.Save(System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\YourShot.jpeg", ImageFormat.Jpeg);
            Bitmap.Save("MovieTemp\\" + name + ".bmp", System.Drawing.Imaging.ImageFormat.Bmp);
        }
    }
}
